package com.ohy.tencent.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ohy.tencent.util.AjaxResult;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;

@RestController
@RequestMapping("bili")
@Slf4j
public class BilibiliController {
    @Value("${windows.video.upload.path}")
    private String windowsUploadPath;

    @Value("${centos.video.upload.path}")
    private String centosUploadPath;

    @Value("${video.visit.path}")
    private String visitPath;

    @Value("${bilibili.cookie}")
    private String bilibiliCookie;

    @PostMapping("getDownloadUrl")
    public String getDownloadUrl(@RequestParam("url") String url) {
        try {
            String bv = "";
            if (url.contains("https://b23.tv")) {
                bv = getRedirectUrl(url);
            } else if (url.contains("bangumi/play")) {
                bv = url;
            } else if (!url.contains("?")) {
                bv = url.substring(url.lastIndexOf("/") + 1);
            } else if (url.contains("https://www.bilibili.com/video")) {
                bv = url.substring(url.indexOf("video") + "video/".length(), url.indexOf("?"));
            }

            if (bv.contains("bangumi/play")) {
                String epId = bv.contains("?") ? bv.substring(bv.indexOf("ep") + 2, bv.indexOf("?")) : bv.substring(bv.indexOf("ep") + 2);
                return downloadByBili(epId, getTitile(bv), bv);
            } else {
                return downloadByUser(bv);
            }
        } catch (Exception e) {
            log.error("获取地址失败", e);
            return "获取地址失败";
        }
    }

    String getTitile(String url) {
        try {
            String res = HttpUtil.get(url);
            return Jsoup.parse(res).title();
        } catch (Exception e) {
            log.error("获取bvid异常【url】:{}", url, e);
            return null;
        }

    }


    String getRedirectUrl(String url) throws IOException {
        HttpURLConnection conn;
        conn = (HttpURLConnection) new URL(url)
                .openConnection();
        conn.setInstanceFollowRedirects(false);
        conn.setConnectTimeout(5000);
        conn.setRequestProperty("user-agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1");
        url = conn.getHeaderField("Location");
        if (url.contains("bangumi/play")) return url;
        url = url.substring(url.indexOf("video") + "video/".length(), url.indexOf("?"));
        return url;
    }

    public static List<JSONObject> sort(List<JSONObject> array) {
        Collections.sort(array, new Comparator<JSONObject>() {
            @Override
            public int compare(JSONObject o1, JSONObject o2) {
                return o1.getDouble("size") > o2.getDouble("size") ? -1 : (o1.getDouble("size") == o2.getDouble("size") ? 0 : 1);
            }
        });
        return array;
    }

    public static String utf8_2_navative(String res) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("js");
        //编写js方法体
        try {
            engine.eval("function getTest(){return  unescape('" + res + "'.replace(/&#x/g, '%u').replace(/;/g, ''));}");
            Invocable invocable = (Invocable) engine;
            Object object = invocable.invokeFunction("getTest");
            return object.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 具体合成视频函数
     *
     * @param videoInputPath 原视频的全路径
     * @param audioInputPath 音频的全路径
     * @param videoOutPath   视频与音频结合之后的视频的路径
     */
    public static void convetor(String videoInputPath, String audioInputPath, String videoOutPath) throws Exception {
        Process process = null;
        InputStream errorStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader br = null;
        try {
            String outLibDir = FileUtil.isWindows() ? "C:\\ProgramData\\Tencent\\ffmpeg\\" : "/opt/video/ffmpeg/";
            String ffmpegPath = outLibDir + (FileUtil.isWindows() ? "ffmpeg-win64-v4.2.2.exe" : "ffmpeg-linux64-v4.1");
            // ffmpeg命令
            String command = ffmpegPath + " -i " + videoInputPath + " -i " + audioInputPath
                    + " -c:v copy -c:a aac -strict experimental " +
                    " -map 0:v:0 -map 1:a:0 "
                    + " -y " + videoOutPath;
            log.info("ffmpeg命令:{}", command);
            process = Runtime.getRuntime().exec(command);
            errorStream = process.getErrorStream();
            inputStreamReader = new InputStreamReader(errorStream);
            br = new BufferedReader(inputStreamReader);
            // 用来收集错误信息的
            String str = "";
            while ((str = br.readLine()) != null) {
                System.out.println(str);
            }
            process.waitFor();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                br.close();
            }
            if (inputStreamReader != null) {
                inputStreamReader.close();
            }
            if (errorStream != null) {
                errorStream.close();
            }
        }
    }

    public String downloadByBili(String epId, String title, String referer) throws Exception {
        String url = "https://api.bilibili.com/pgc/player/web/playurl?qn=112&otype=json&fourk=1&ep_id=" + epId + "&fnver=0&fnval=80";
        String res = HttpRequest.get(url)
                .header("cookie", bilibiliCookie)
                .header("referer", referer)
                .execute().body();
        String videoUrl = JSONObject.parseObject(res).getJSONObject("result").getJSONObject("dash").getJSONArray("video").getJSONObject(0).getString("baseUrl");
        String audioUrl = JSONObject.parseObject(res).getJSONObject("result").getJSONObject("dash").getJSONArray("audio").getJSONObject(0).getString("baseUrl");
        String videoPath = UUID.randomUUID() + ".mp4";
        String audioPath = UUID.randomUUID() + ".mp4";
        String allVideo = title.replaceAll("：", "");
        allVideo = allVideo.substring(0, allVideo.indexOf("_")) + ".mp4";

        String uploadPath = FileUtil.isWindows() ? windowsUploadPath : centosUploadPath;

        if (FileUtil.isWindows()) FileUtil.mkParentDirs(new File(uploadPath + videoPath));

        FileUtils.copyURLToFile(new URL(videoUrl), new File(uploadPath + videoPath));
        FileUtils.copyURLToFile(new URL(audioUrl), new File(uploadPath + audioPath));

        convetor(uploadPath + videoPath, uploadPath + audioPath, uploadPath + allVideo);

        FileUtil.del(uploadPath + videoPath);
        FileUtil.del(uploadPath + audioPath);
        log.info(allVideo + "下载成功");
        return uploadPath + allVideo;
    }


    public String downloadByUser(String bv) throws Exception {

        String baseUrl = "https://app.bilibili.com/x/v2/search?build=5490400&fnval=16&mobi_app=android&keyword=" + bv;
        if (Objects.isNull(bv)) return "未查询到该视频";
        String res = HttpUtil.get(baseUrl);
        JSONObject jsonObject = JSONObject.parseObject(res);
        res = jsonObject.getJSONObject("data").getJSONArray("item").getJSONObject(0).getString("uri");
        res = res.substring(res.indexOf("%7B%22"), res.lastIndexOf("%7D") + 3);
        res = utf8_2_navative(res);
        jsonObject = JSONObject.parseObject(res);
        JSONArray videoArr = jsonObject.getJSONObject("dash").getJSONArray("video");
        List<JSONObject> videoList = new ArrayList<>();
        for (int i = 0; i < videoArr.size(); i++) {
            videoList.add(videoArr.getJSONObject(i));
        }
        sort(videoList);

        JSONArray audioArr = jsonObject.getJSONObject("dash").getJSONArray("audio");
        List<JSONObject> audioList = new ArrayList<>();
        for (int i = 0; i < audioArr.size(); i++) {
            audioList.add(audioArr.getJSONObject(i));
        }
        sort(audioList);
        String videoUrl = videoList.get(0).getString("base_url");

        String audioUrl = audioList.get(0).getString("base_url");
        String videoPath = UUID.randomUUID() + ".m4s";
        String audioPath = UUID.randomUUID() + ".m4s";
        String allVideo = UUID.randomUUID() + ".mp4";

        String uploadPath = FileUtil.isWindows() ? windowsUploadPath : centosUploadPath;

        if (FileUtil.isWindows()) FileUtil.mkParentDirs(new File(uploadPath + videoPath));

//        FileUtils.copyURLToFile(new URL(videoUrl), new File(uploadPath + videoPath));
//        FileUtils.copyURLToFile(new URL(audioUrl), new File(uploadPath + audioPath));

        httpDownload(audioUrl, uploadPath + audioPath);
        httpDownload(videoUrl, uploadPath + videoPath);

        convetor(uploadPath + videoPath, uploadPath + audioPath, uploadPath + allVideo);
        FileUtil.del(uploadPath + videoPath);
        FileUtil.del(uploadPath + audioPath);
        return visitPath + allVideo;
    }

    public boolean httpDownload(String httpUrl, String saveFile) {
        int byteRead;
        URL url;
        try {
            url = new URL(httpUrl);
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
            return false;
        }
        try {
            URLConnection conn = url.openConnection();
            InputStream inStream = conn.getInputStream();
            FileOutputStream fs = new FileOutputStream(saveFile);
            byte[] buffer = new byte[1024];
            while ((byteRead = inStream.read(buffer)) != -1) {
                fs.write(buffer, 0, byteRead);
            }
            inStream.close();
            fs.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

}
